import { useEffect, useMemo, useRef, useState } from 'react';
import { flushSync } from 'react-dom';
const VirtualList = (props) => {
  const {
    width,
    height,
    style = {},
    className = '',
    itemCount,
    itemSize,
    overscan = 5,
    Item,
    hasNextPage = false,
    hasLastPage = false,
    getNextData = () => {},
    getLastData = () => {},
  } = props;
  const extra = overscan > 0 ? overscan : 1;

  const list = useMemo(() => Array.from(Array(itemCount).keys()), [itemCount]);
  const showCount = useMemo(() => Math.floor(height / itemSize), [height, itemSize]);

  const [start, setStart] = useState(0);
  const [end, setEnd] = useState(showCount);
  const [requestLoading, setRequestLoading] = useState(false);

  const ref = useRef(null);
  const handleScroll = useRef(null);

  const loadMoreFn = async (direction: 'next' | 'last') => {
    if (requestLoading) return;
    flushSync(() => {
      setRequestLoading(true);
    });
    if (direction === 'next' && hasNextPage) {
      await getNextData();
    } else if (direction === 'last' && hasLastPage) {
      await getLastData();
    }
    setRequestLoading(false);
  };

  useEffect(() => {
    ref.current?.removeEventListener('scroll', (e) => handleScroll.current(e));
    handleScroll.current = (e) => {
      const currentStart = Math.floor(e.target.scrollTop / itemSize) + 1;
      flushSync(() => {
        setStart(currentStart);
        setEnd(currentStart + showCount);
      });
      if (e.target.scrollTop === 0 && hasLastPage) {
        loadMoreFn('last');
      }
    };
    ref.current.addEventListener('scroll', (e) => handleScroll.current(e));
    return ref.current?.removeEventListener('scroll', (e) => handleScroll.current(e));
  }, [hasLastPage, showCount]);

  useEffect(() => {
    if (end + showCount >= itemCount && hasNextPage) {
      loadMoreFn('next');
    }
  }, [end, hasNextPage, showCount]);

  useEffect(() => {
    flushSync(() => {
      setEnd(start + showCount);
    });
  }, [showCount]);

  return (
    <div className={className} style={{ ...style, width, height, overflow: 'auto', position: 'relative' }} ref={ref}>
      <div style={{ height: `${itemCount * itemSize}px` }} key={start}>
        {list.map((item, index) => {
          if (index < start - extra || index >= end + extra) {
            return null;
          }
          const style = {
            position: 'absolute',
            top: `${item * itemSize}px`,
            left: 0,
          };
          return Item({ index, style });
        })}
      </div>
    </div>
  );
};
export default VirtualList;
